#include "editcollisionshapesview.h"
#include "../../physics/collisionshapes.h"
#include "../../physics/collisionshapesdocument.h"
#include "../../gameobjects/texture.h"
#include "../../gameobjects/animation.h"
#include "../../gameobjects/graphicsobject.h"
#include "../../physics/collisionshapetools.h"
#include "../../physics/shapeutils.h"
#include <QPainter>
#include <QWheelEvent>
#include <QKeyEvent>

EditCollisionShapesView::EditCollisionShapesView(QWidget *parent) :
    QWidget(parent)
  , m_backgroundColor(Qt::black)
  , m_gridColor(Qt::blue)
  , m_fGridSpacing(30)
  , m_bShowGrid(true)
  , m_fZoomFactor(1.0f)
  , m_bPanning(false)
{
    m_viewOrigin = QPointF(rect().width()/2, rect().height()/2);
    updateHelperGrid();

    setFocusPolicy(Qt::StrongFocus);
}

EditCollisionShapesView::~EditCollisionShapesView()
{
    if(m_currentTool)
    {
        m_currentTool->exit();
    }
}

void EditCollisionShapesView::setCollShapesDoc(QPointer<CollisionShapesDocument> doc)
{
    m_csDoc = doc;

    connect(m_csDoc, SIGNAL(editingObjectChanged()),
            this, SLOT(updateView()));

    connect(m_csDoc, SIGNAL(shapeSelectionChanged()),
            this, SLOT(updateView()));

    connect(m_csDoc, SIGNAL(shapeAdded(QPointer<CollisionShape>)),
            this, SLOT(updateView()));

    connect(m_csDoc, SIGNAL(shapeRemoved(QPointer<CollisionShape>)),
            this, SLOT(updateView()));
}

QPointer<CollisionShapesDocument> EditCollisionShapesView::collisionShapesDocument() const
{
    return m_csDoc;
}

void EditCollisionShapesView::paintEvent(QPaintEvent *)
{
    QPainter painter(this);

    if(m_bShowGrid)
    {
        painter.drawPixmap(0, 0, m_pixGrid);
    }

    painter.translate(m_viewOrigin);
    painter.scale(m_fZoomFactor, m_fZoomFactor);

    drawGraphicsObjectImage(painter);
    drawShapes(painter);

    painter.scale(1.0f/m_fZoomFactor, 1.0f/m_fZoomFactor);
    drawAxis(painter);
}

void EditCollisionShapesView::resizeEvent(QResizeEvent *pEvent)
{
    m_viewOrigin = QPointF(rect().width()/2, rect().height()/2);
    updateHelperGrid();
}

void EditCollisionShapesView::updateHelperGrid()
{
    m_pixGrid = QPixmap(rect().width(), rect().height());
    QPainter painter(&m_pixGrid);
    painter.fillRect(rect(), m_backgroundColor);

    painter.setOpacity(0.5);
    painter.setPen(m_gridColor);

    float w = rect().width();
    float h = rect().height();

    int numW = w/m_fGridSpacing;
    int numH = h/m_fGridSpacing;

    for(int i=1;i<numW + 1;i++)
    {
        float x = i*m_fGridSpacing;
        QPointF topPoint(x, 0);
        QPointF bottomPoint(x, rect().height());

        painter.drawLine(topPoint, bottomPoint);
    }

    for(int i=1;i<numH + 1;i++)
    {
        float y = i*m_fGridSpacing;
        QPointF leftPoint(0, y);
        QPointF rightPoint(rect().width(), y);

        painter.drawLine(leftPoint, rightPoint);
    }
}

void EditCollisionShapesView::drawGraphicsObjectImage(QPainter &painter)
{
    Texture* texture = 0;
    QRect frameRect;
    bool isAnimation;

    m_csDoc->currentGraphicsParameters(&texture, frameRect, isAnimation);

    if(!texture)
    {
        // do nothing
    }
    else if(!isAnimation)
    {
        QPixmap pm = texture->pixmap();
        painter.drawPixmap(-pm.rect().width()/2, -pm.rect().height()/2, pm);
    }
    else if(!frameRect.isNull())
    {
        QPixmap pm = texture->pixmap();
        painter.drawPixmap(QPoint(-frameRect.width()/2,
                      -frameRect.height()/2), pm, frameRect);
    }
}

void EditCollisionShapesView::drawShapes(QPainter &painter)
{
    const QList<QPointer<CollisionShape> >& cShapes = m_csDoc->currentCollisionShapes();

    foreach(QPointer<CollisionShape> shape, cShapes)
    {
        shape->draw(m_csDoc, painter);
    }

    if(m_currentTool)
    {
        m_currentTool->draw(painter);
    }
}

void EditCollisionShapesView::mousePressEvent(QMouseEvent *pEvent)
{
    if(pEvent->button() == Qt::MiddleButton)
    {
        m_bPanning = true;
        m_savedCursor = cursor();
        setCursor(Qt::ClosedHandCursor);
    }
    else if(m_currentTool)
    {
        // IMPORTANT: Tool have to update view
        m_currentTool->mousePress(pEvent);
    }

    if(pEvent->button() == Qt::RightButton)
    {
        Texture* texture = 0;
        QRect frameRect;
        bool isAnimation;
        QPixmap pm;
        QRect pmRect;

        m_csDoc->currentGraphicsParameters(&texture, frameRect, isAnimation);
        pm = texture->pixmap();

        if(isAnimation && !frameRect.isNull())
        {
            pmRect = frameRect;
        }
        else
        {
            pmRect = pm.rect();
        }

        calculateSilhouette(m_csDoc, pm, pmRect);
    }

    m_oldMousePos = pEvent->posF();
}

void EditCollisionShapesView::mouseMoveEvent(QMouseEvent *pEvent)
{
    if(m_bPanning)
    {
        QPointF offset = pEvent->posF() - m_oldMousePos;
        m_viewOrigin += offset;
        update();
    }
    else if(m_currentTool)
    {
        // IMPORTANT: Tool have to update view
        m_currentTool->mouseMove(pEvent);
    }

    m_oldMousePos = pEvent->posF();
}

void EditCollisionShapesView::mouseReleaseEvent(QMouseEvent *pEvent)
{
    m_oldMousePos = pEvent->posF();

    if(m_bPanning)
    {
        m_bPanning = false;
        setCursor(m_savedCursor);
    }
    else if(m_currentTool)
    {
        // IMPORTANT: Tool have to update view
        m_currentTool->mouseRelease(pEvent);
    }
}

void EditCollisionShapesView::keyPressEvent(QKeyEvent *pEvent)
{
    if(pEvent->key() == Qt::Key_Delete)
    {
        m_csDoc->deleteSelection();
        pEvent->accept();
        update();
    }
}

void EditCollisionShapesView::wheelEvent(QWheelEvent *pEvent)
{
    int delta = pEvent->delta();
    float inc = (float)delta/(120.0f*3);

    m_fZoomFactor += inc;
    if(m_fZoomFactor < 0.1f) m_fZoomFactor = 0.1f;
    update();
}

void EditCollisionShapesView::setGridColor(const QColor &color)
{
    m_gridColor = color;
    updateHelperGrid();
    update();
}

void EditCollisionShapesView::setBackgroundColor(const QColor &color)
{
    m_backgroundColor = color;
    updateHelperGrid();
    update();
}

void EditCollisionShapesView::setShowGrid(bool bShow)
{
    m_bShowGrid = bShow;
    update();
}

void EditCollisionShapesView::setGridSpacing(float size)
{
    m_fGridSpacing = size;
    updateHelperGrid();
    update();
}

void EditCollisionShapesView::drawAxis(QPainter &painter)
{
    QSize size(rect().width()/10, rect().height()/10);

    painter.setPen(QPen(Qt::green, 1));
    painter.drawLine(0, 0, size.width(), 0);
    painter.drawText(size.width(), 10, "x");

    painter.setPen(QPen(Qt::red, 1));
    painter.drawLine(0, 0, 0, -size.height());
    painter.drawText(2, -size.height(), "y");

    painter.setPen(Qt::yellow);
    painter.drawText(-8, 8, "0");
}

void EditCollisionShapesView::updateView()
{
    update();
}

void EditCollisionShapesView::zoomIn()
{
    m_fZoomFactor += 0.1f;
    update();
}

void EditCollisionShapesView::zoomOut()
{
    m_fZoomFactor -= 0.1f;
    if(m_fZoomFactor < 0.1f) m_fZoomFactor = 0.1f;
    update();
}

void EditCollisionShapesView::resetView()
{
    m_fZoomFactor = 1.0f;
    m_viewOrigin = QPointF(rect().width()/2, rect().height()/2);
    update();
}

void EditCollisionShapesView::setCurrentTool(QPointer<CollisionShapeTool> curTool)
{
    if(m_currentTool)
    {
        m_currentTool->exit();
    }

    m_currentTool = curTool;

    m_currentTool->enter();
}


float EditCollisionShapesView::zoomFactor() const
{
    return m_fZoomFactor;
}

QPointF EditCollisionShapesView::viewOrigin() const
{
    return m_viewOrigin;
}
